from system.dynload import dynload
from system.eventServer import eventServer

import yaml
from copy import copy

class Object:
    fileCache = {}
    
    def __init__(self, parent, type = None, name = None):
        self.parent = parent
        
        if not type:
            type = "object"
            
        if not name:
            name = type
        
        if self.parent:
            realName = self.parent.makeChildName(name)
            self.parent.children[realName] = self
        else:
            realName = "root"
            
        if not type:
            type = realName
        
        self.dirtyVars = []
        
        self.vars = {}
        self.children = {}
        self.includes = []
        self.childLoadData = []
        
        self.loadFunctions = {"children":self.loadChildren, "vars":self.loadVars, "include":self.loadIncludes}
        self.saveFunctions = {"children":self.saveChildren, "vars":self.saveVars, "include":self.saveIncludes, "type":self.saveType}
        
        self.setVar("name",realName)
        self.setVar("type",type)
        self.setVar("path",self.makePath())
        
        if self.parent and self.parent.childLoadData:
            for data in self.parent.childLoadData:
                self.loadObject(data)
            self.parent.childLoadData = []
        
        self.load(type)
        
    def __del__(self):
        if self.parent:
            self.parent.removeChild(self.getVar('name'))
            
    def addChildData(self, data):
        self.childLoadData.append(data)
            
    def log(self, s):
        print "%s: %s" % (self.getVar("path"), s)
            
    def hasVar(self, name):
        return self.vars.has_key(name)
    
    def getVar(self, name):
        if self.hasVar(name):
            return self.vars[name]
        else:
            return None
        
    def setVar(self, name, val):
        self.vars[name] = val
        if name not in self.dirtyVars:
            self.dirtyVars.append(name)
            
    def defaultVar(self, name, val):
        if not self.hasVar(name):
            self.setVar(name,val)
            
    def clearVars(self):
        self.vars.clear()
            
    def getDirtyVars(self):
        return self.dirtyVars
    
    def clearDirtyVars(self):
        self.dirtyVars.clear()
        
    def addChild(self, type = None, name = None):
        if not type:
            type = 'object'
        if not name:
            name = type
            
        return (dynload(type) or Object)(self,type,name)
        
    def removeChild(self, name):
        if self.hasChild(name):
            child = self.children.pop(name)
            child.parent = None
            child.unregister()
            del child
            return True
        else:
            return False
        
    def unregister(self):
        eventServer.unregisterObject(self)
        for child in self.children.itervalues():
            child.unregister()
        
    def hasChild(self, name):
        return self.children.has_key(name)
    
    def getChild(self, name):
        return self.children[name]
    
    def clearChildren(self):
        for name in self.children.keys():
            self.removeChild(name)
        self.children.clear()
        
    def makeChildName(self, base):
        if not base:
            base = 'object'
            
        if not self.children.has_key(base):
            return base
        else:
            i = 2
            name = '%s_%d' % (base,i)
            while self.children.has_key(name):
                i += 1
                name = '%s_%d' % (base,i)
            return name
        
    def makePath(self):
        if not self.parent:
            return "/"
        elif not self.parent.parent:
            return "/" + self.getVar("name")
        else:
            return self.parent.getVar("path") + "/" + self.getVar("name")
        
    def load(self, type = None):
        if not type:
            type = self.getVar("type")
            
        #type.replace(".","/")
        
        filename = 'dat/objects/%s.yml' % type
        
        if Object.fileCache.has_key(filename):
            fileData = copy(Object.fileCache[filename])
        else:
            
            try:
                fp = open(filename,'r')
            except:
                return False
            
            fileData = yaml.evalLoad(fp)
            fp.close()
            Object.fileCache[filename] = copy(fileData)
        
        self.loadObject(fileData)
        
        return True
            
    def loadObject(self, fileData):
        for key, data in fileData.iteritems():
            if self.loadFunctions.has_key(key):
                self.loadFunctions[key](key, data)
                
    def loadChildren(self, key, data):
        for childName, childData in data.iteritems():
            if self.hasChild(childName):
                self.getChild(childName).loadObject(childData)
            else:
                if childData.has_key("type"):
                    childType = childData["type"]
                else:
                    childType = childName
                #self.log("add child %s %s" % (childType, childName))
                self.childLoadData = [childData]
                self.addChild(childType,childName)
                
    def loadIncludes(self, key, data):
        for type in data:
            if self.load(type):
                self.includes.append(type)
                
    def loadVars(self, key, data):
        #self.vars.update(data)
        
        for key, val in data.iteritems():
            if key not in ['name','type','path']:
                self.vars[key] = val
            
    def save(self, type = None):
        if not type:
            type = self.getVar("type")
            
        type.replace(".","/")
        
        filename = 'dat/objects/%s.yml' % type
        
        yaml.dump(self.saveObject(),open(filename,'w'))
        
        return True
            
    def saveObject(self):
        fileData = {}
        for key, function in self.saveFunctions.iteritems():
            data = function(key)
            if data:
                fileData[key] = data
        return fileData
            
    def saveChildren(self, key):
        data = {}
        for childName, child in self.children.iteritems():
            data[childName] = child.saveObject()
        return data
        
    def saveVars(self, key):
        data = {}
        for key, val in self.vars.iteritems():
            if key not in ['name','path','type']:
                data[key] = val
        return data
    
    def saveIncludes(self, key):
        return self.includes
    
    def saveType(self, key):
        if self.getVar("name") != self.getVar("type"):
            return self.getVar("type")
        else:
            return None
    
    def getArg(self, name, default, varDict = None):
        if varDict and varDict.has_key(name):
            return varDict[name]
        elif not varDict and self.hasVar(name):
            return self.getVar(name)
        else:
            return default
        
    def getRoot(self):
        if not self.parent:
            return self
        else:
            return self.parent.getRoot()
        
    def search(self, var, value, fromRoot = True):
        if fromRoot:
            return self.getRoot().search(var,value,False)
        else:
            if self.hasVar(var) and self.getVar(var) == value:
                return self
            else:
                for child in self.children.itervalues():
                    result = child.search(var,value,fromRoot)
                    if result:
                        return result
                return None
                
    def searchAll(self, var, value, fromRoot = True):
        if fromRoot:
            return self.getRoot().search(var,value,False)
        else:
            result = []
            if self.hasVar(var) and self.getVar(var) == value:
                result.append(self)
            for child in self.children.itervalues():
                result.extend(child.searchAll(var,value,fromRoot))
            return result
            
    def getAncestors(self):
        result = [self]
        if self.parent:
            result.extend(self.parent.getAncestors())
        return result
        
    def searchAncestors(self, var, value):
        if self.parent:
            if self.parent.hasVar(var) and self.parent.getVar(var) == value:
                return self.parent
            else:
                return self.parent.searchAncestors(var,value)
        else:
            return None
        
    def getAncestorVars(self, name):
        result = [self.getVar(name)]
        if self.parent:
            result.insert(0,self.parent.getAncestorVars(name))
        return result
        
    def flattenVars(self, name):
        result = []
        if self.hasVar(name):
            var = self.getVar(name)
            if type(var) == list:
                result.extend(var)
            else:
                result.append(var)
        for child in self.children.itervalues():
            result.extend(child.flattenVars(name))
        return result
            
    def getObject(self, path):
        if path == "/":
            return self.getRoot()
        elif path.startswith("/"):
            return self.getRoot().getObject(path[1:])
        elif "/" in path:
            pos = path.find("/")
            start = path[:pos]
            rest = path[pos+1:]
            if start == ".":
                return self.getObject(rest)
            elif start == "..":
                return self.parent.getObject(rest)
            elif self.hasChild(start):
                return self.getChild(start).getObject(rest)
            else:
                return None
        else:
            if path == ".":
                return self
            elif path == "..":
                return self.parent
            elif self.hasChild(path):
                return self.getChild(path)
            else:
                return None
